from PIL import Image
import numpy as np

def binarize(image, threshold):
    #二值化灰度图
    width, height = image.size
    for x in range(width):
        for y in range(height):
            pixel = image.getpixel((x, y))
            if pixel < threshold:
                image.putpixel((x, y), 0)
            else:
                image.putpixel((x, y), 255)
    return image

def preimg(path):
    # 预处理待加密图片，转为二值图并修改大小为1000*1000
    img=Image.open(path)
    img=img.convert("L")
    img = img.resize((1000, 1000))
    img = binarize(img, 150)
    mat = np.array(img)
    return mat

def logistic(X0, r):
  return r * X0 * (1 - X0)

def chaos_sequence(x0, r, T, n):
    #产生混沌序列，用以加密
    sequence = []
    for i in range(n):
        x0 = logistic(x0, r)
        if x0 < T:
            sequence.append(0)
        else:
            sequence.append(1)
    return sequence

def encry(mpath,cpath,key):
    mmat=preimg(mpath)
    sequence = chaos_sequence(key, 3.99, 0.5, 1000*1000)
    sequence = np.array(sequence)
    data_mat = mmat.reshape((mmat.size,))
    mat = (np.logical_xor(data_mat, sequence).astype(np.uint8) * 255)

    carrier_img = Image.open(cpath)
    carrier_array = np.array(carrier_img)
    if 3 * carrier_array.shape[0] * carrier_array.shape[1] < mat.size:
        print("Cant encry!")
        return -1
    else:
        count = 0
        for p in range(3):
            for i in range(carrier_array.shape[0]):
                for j in range(carrier_array.shape[1]):
                    if count >= mat.size:
                        break
                    if mat[count] == 255:
                        carrier_array[i, j, p] = carrier_array[i, j, p] | 0x01
                    else:
                        carrier_array[i, j, p] = carrier_array[i, j, p] & 0xFE
                    count = count + 1

    image_result = Image.fromarray(carrier_array)
    image_result.save("加密图片.bmp")

def decrypt(imagePath,key):
    imglist=[]
    count=0
    carrier_array = np.array(Image.open(imagePath))
    sequence = chaos_sequence(key, 3.99, 0.5, 1000*1000)
    for p in range(3):
        for i in range(carrier_array.shape[0]):
            for j in range(carrier_array.shape[1]):
                if count >= 1000*1000:
                    break
                imglist.append(carrier_array[i, j, p] & 0x01)
                count = count + 1
    imgarray = np.array(imglist)
    mat_result = (np.logical_xor(imgarray,sequence).astype(np.uint8) * 255).reshape((1000, 1000))
    Image.fromarray(mat_result).save("解密图片.bmp")

if __name__ == '__main__':
    messageImagepath="待加密.jpg"
    key=0.451
    carrierPath="载体.jpg"
    confidentialImage="加密图片.bmp"

    encry(messageImagepath,carrierPath,key)
    decrypt(confidentialImage,0.451)

